// -----------------------------------------------------------
// Game Framework
// Copyright © 2024-2024 deadcycle. All rights reserved.
// Homepage: https://deadcycle.gitee.io
// Feedback: deadcycle@163.com
// -----------------------------------------------------------

using System;
using System.Collections.Generic;

using UnityEngine;
using UnityEngine.Pool;

namespace UIWidget
{
    /// <summary>
    /// 不定长的滚动视图控件(Cell高度不一致, 可变长的, 不支持网格布局)。
    /// 使用须知:
    /// 1.  Cell实例对象的管理模式有三种:
    ///         * 静态视图, 固定排布
    ///         * 支持复用 (对象池模式, 使用开关开启)
    ///         * 自定义管理 (预留模式, 慎用)
    ///
    /// 2.  静态视图, 固定排布
    ///         * Cell尺寸运行时确定 必须设置 `SetGetSizeFunc` 
    ///         * Cell尺寸静态, 可配置, 也可通过 `SetGetSizeFunc`
    ///         * 尺寸与模板配置, 数量需要一致
    /// 
    /// 3.  复用模式
    ///         * 调用 `SetGetTemplateFunc` 返回cell索引对应的预制体模板的索引 (必选)
    ///         * 调用 `SetGetSizeFunc` 返回Cell索引对应的尺寸size (必选)
    /// 
    /// 4.  需要运行时, 动态控制Cell的渲染更新, 需要设置 `SetGetScriptTypeFunc`
    /// 
    /// </summary>
    public class VariableScrollRect : ScrollRectBase
    {
        #region 字段
        private static int s_CachedTemplateIndex = -1;

        [SerializeField]
        private List<Vector2> m_CellSizes = new List<Vector2>();

        [SerializeField]
        private List<GameObject> m_CellTemplates = new List<GameObject>();

        [SerializeField]
        private bool m_UsingObjectPool;

        private List<ObjectPool<RectTransform>> m_CellInstancePools;
        private RectTransform[] m_CachedObjects;
        private Dictionary<RectTransform, int> m_CellInstanceIndex;

        private readonly Dictionary<RectTransform, CellBase> m_CellScripts =
            new Dictionary<RectTransform, CellBase>();

        private Func<int, Vector2> m_CellGetSizeFunc;
        private Func<int, int> m_CellGetTemplateFunc;
        private Func<int, Type> m_CellGetScriptTypeFunc;
        #endregion

        private Dictionary<RectTransform, int> CellInstanceIndex =>
            m_CellInstanceIndex ??= new Dictionary<RectTransform, int>();

        #region override重写函数
        /// <summary>
        /// 获取Cell的Size。
        /// </summary>
        /// <param name="index">Cell的索引。</param>
        protected override Vector2 GetCellSize(int index)
        {
            if (m_CellGetSizeFunc == null)
            {
                if (index < m_CellSizes.Count)
                {
                    return m_CellSizes[index];
                }

                Debug.LogError($"Cell尺寸没有配置 索引: {index}");

                return Vector2.zero;
            }

            try
            {
                return m_CellGetSizeFunc(index);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            return Vector2.zero;
        }

        /// <summary>
        /// 初始化实例对象池。
        /// </summary>
        protected override void InitInstancePool()
        {
            // 使用自定义管理Cell实例对象模式
            if (m_GetInstanceFunc != null || m_RecycleInstanceFunc != null)
            {
                return;
            }

            // 不复用对象模式
            if (!m_UsingObjectPool)
            {
                m_CachedObjects = new RectTransform[m_CellTemplates.Count];

                return;
            }

            // 使用对象池
            if (m_CellInstancePools != null)
            {
                return;
            }

            // 根据模板数量, 创建对应数量的对象池
            m_CellInstancePools = new List<ObjectPool<RectTransform>>();

            for (int i = 0; i < m_CellTemplates.Count; i++)
            {
#if UNITY_EDITOR
                m_CellInstancePools.Add(new ObjectPool<RectTransform>(OnPoolCreateInstance,
                    actionOnRelease: OnPoolReleaseInstance,
                    actionOnDestroy: OnPoolDestroyInstance,
                    collectionCheck: k_EnableDebug,
                    defaultCapacity: m_PoolCapacity));
#else
                m_CellInstancePools.Add(new ObjectPool<RectTransform>(OnPoolCreateInstance,
                    actionOnRelease: OnPoolReleaseInstance,
                    actionOnDestroy: OnPoolDestroyInstance,
                    collectionCheck: false,
                    defaultCapacity: m_PoolCapacity));
#endif
            }
        }

        /// <summary>
        /// 清理实例对象池。
        /// </summary>
        protected override void ClearInstancePool()
        {
            if (m_CellInstancePools == null)
            {
                return;
            }

            foreach (ObjectPool<RectTransform> cellObjectPool in m_CellInstancePools)
            {
                cellObjectPool.Clear();
            }

            m_CellInstancePools = null;
        }

        /// <summary>
        ///  对象池获取对象时, 需要创建新的对象时回调。
        /// </summary>
        protected override RectTransform OnPoolCreateInstance()
        {
            GameObject cellTemplate = m_CellTemplates[s_CachedTemplateIndex];

            GameObject instance = Instantiate(cellTemplate, content.transform);
            RectTransform rectTransform = instance.GetComponent<RectTransform>();

            rectTransform.anchorMin = Vector2.up;
            rectTransform.anchorMax = Vector2.up;
            rectTransform.pivot = Vector2.zero;

            // 记录实例对应的 对象池索引
            CellInstanceIndex.Add(rectTransform, s_CachedTemplateIndex);

            if (!instance.activeSelf)
            {
                instance.SetActive(true);
            }

            return rectTransform;
        }

        /// <summary>
        /// 获取Cell对象
        /// </summary>
        /// <param name="index"></param>
        protected override RectTransform GetCellInstance(int index)
        {
            RectTransform cellObject = null;

            if (GetCellInstanceCustom(index, ref cellObject))
            {
                // 从自定义函数中来获取Cell对象实例
            }
            else
            {
                // 使用对象池, 复用对象
                if (m_UsingObjectPool)
                {
                    if (m_CellGetTemplateFunc == null)
                    {
                        throw new Exception(
                            "Use object pool need `SetCellTemplateFunc` (使用对象池模式, 需要调用SetCellTemplateFunc)");
                    }

                    try
                    {
                        int templateIndex = m_CellGetTemplateFunc(index);

                        if (templateIndex < m_CellTemplates.Count)
                        {
                            s_CachedTemplateIndex = templateIndex;

                            cellObject = m_CellInstancePools[templateIndex].Get();
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e);
                    }
                }
                else
                {
                    if (index >= m_CellTemplates.Count)
                    {
                        throw new OverflowException("Index is out of range.(静态视图索引越界)");
                    }

                    // 从模板实例新的实例
                    if (m_CachedObjects[index] == null)
                    {
                        GameObject cellTemplate = m_CellTemplates[index];

                        if (cellTemplate == null)
                        {
                            throw new NullReferenceException(
                                $"Cell [{index}] template is null.(预制体模板为空)");
                        }

                        GameObject instance = Instantiate(cellTemplate, content.transform);
                        RectTransform rectTransform = instance.GetComponent<RectTransform>();

                        rectTransform.anchorMin = Vector2.up;
                        rectTransform.anchorMax = Vector2.up;
                        rectTransform.pivot = Vector2.zero;

                        if (!instance.activeSelf)
                        {
                            instance.SetActive(true);
                        }

                        m_CachedObjects[index] = rectTransform;
                    }
                    else
                    {
                        // 已缓存实例, 不在视口范围时, 在Pool节点下
                        // 如果性能有极大瓶颈, 可不修改其父节点
                        m_CachedObjects[index].SetParent(content.transform);
                    }

                    cellObject = m_CachedObjects[index];
                }
            }

            return cellObject;
        }

        /// <summary>
        /// 回收Cell对象。
        /// </summary>
        /// <param name="instance">Cell对象。</param>
        protected override void RecycleCellInstance(RectTransform instance)
        {
            // 自定义回收
            if (RecycleInstanceCustom(instance))
            {
                return;
            }

            if (m_UsingObjectPool)
            {
                if (!m_CellInstanceIndex.TryGetValue(instance, out int templateIndex))
                {
                    throw new Exception("Get template index failed. (获取模板索引失败)");
                }

                m_CellInstancePools[templateIndex].Release(instance);
            }
            else
            {
                // 静态视图, Cell实例被回收的根本原因在于不在视口范围内
                // 如果性能有极大瓶颈, 可不修改其父节点
                instance.SetParent(m_PoolTransform);
            }
        }

        /// <summary>
        /// 将复用Cell实例分配给对应索引。 
        /// </summary>
        /// <param name="index">索引值。</param>
        /// <param name="cellInstance">复用Cell实例。</param>
        protected override void SetInstanceForIndex(int index, RectTransform cellInstance)
        {
            base.SetInstanceForIndex(index, cellInstance);

            if (m_CellGetScriptTypeFunc == null)
            {
                return;
            }

            Type cellScriptType = null;

            try
            {
                cellScriptType = m_CellGetScriptTypeFunc(index);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }

            if (cellScriptType == null)
            {
                return;
            }

            if (!m_CellScripts.TryGetValue(cellInstance, out CellBase cellScript))
            {
                if (!cellInstance.TryGetComponent(out cellScript))
                {
                    cellScript = (CellBase)cellInstance.gameObject.AddComponent(cellScriptType);
                }

                if (cellScript != null)
                {
                    m_CellScripts.Add(cellInstance, cellScript);
                }
            }

            m_ManagedCellDatas[index].CellScript = cellScript;
        }
        #endregion

        /// <summary>
        /// 设置自定义获取Cell尺寸的回调函数。
        /// </summary>
        public void SetGetSizeFunc(Func<int, Vector2> cellSizeFunc)
        {
            if (cellSizeFunc == null)
            {
                throw new ArgumentNullException(nameof(cellSizeFunc));
            }

            m_CellGetSizeFunc = cellSizeFunc;
        }

        /// <summary>
        /// 设置获取索引对应的模板编号的回调函数。
        /// </summary>
        public void SetGetTemplateFunc(Func<int, int> cellGetTemplateFunc)
        {
            if (cellGetTemplateFunc == null)
            {
                throw new ArgumentNullException(nameof(cellGetTemplateFunc));
            }

            m_CellGetTemplateFunc = cellGetTemplateFunc;
        }

        /// <summary>
        /// 设置获取索引对应的渲染刷新脚本的回调函数。
        /// </summary>
        public void SetGetScriptTypeFunc(Func<int, Type> cellGetScriptTypeFunc)
        {
            if (cellGetScriptTypeFunc == null)
            {
                throw new ArgumentNullException(nameof(cellGetScriptTypeFunc));
            }

            m_CellGetScriptTypeFunc = cellGetScriptTypeFunc;
        }
    }
}